<!DOCTYPE HTML>
<html>
<head>
<script>
    if (window.testRunner) {
        testRunner.dumpAsText();
        testRunner.waitUntilDone();
    }

    if (window.internals)
        internals.settings.setPreferCompositingToLCDTextEnabled(false);

    function hasRepaint(layerTree) {
        var layers = JSON.parse(layerTree).layers;
        for (var i = 0; i < layers.length; ++i) {
            var layer = layers[i];
            // Exclude repaint in the vertical scrollbar layer which always happens on scroll.
            if (layer.name == "Vertical Scrollbar Layer")
                continue;
            if (layer.invalidations)
                return true;
        }
        return false;
    }

    function rAF() {
      return new Promise(function(resolve) {
        requestAnimationFrame(resolve);
      });
    }

    async function testScrollRepaint(description, expectsRepaint, scroller) {
        var result = description + ":\n";

        // Ensure that any dirtiness has been committed
        await rAF();
        await rAF();

        if (window.internals)
            internals.startTrackingRepaints(document);

        scroller.scrollTop = 100;

        // Wait until the next frame reflecting the new scroll position.
        await rAF();

        if (window.internals) {
            var layer_tree = internals.layerTreeAsText(document, internals.LAYER_TREE_INCLUDES_INVALIDATIONS);
            var repainted = hasRepaint(layer_tree);
            var repaintedMessage = repainted ? "repainted" : "did not repaint";
            var expectedMessage = expectsRepaint ? " when expected" : " when unexpected";
            if (repainted == expectsRepaint)
                result += "PASS " + repaintedMessage + expectedMessage + "\n";
            else
                result += "FAIL " + repaintedMessage + expectedMessage + "\n" + layer_tree + "\n";
            internals.stopTrackingRepaints(document);
        }

        // Do all cleanup here (so as not to affect repaint rects).
        scroller.scrollTop = 0;
        return result;
    }

    async function testNoRepaint() {
        return testScrollRepaint("Overflow scroll", false, container);
    }

    async function testWithSelection() {
        var selection = getSelection();

        var range = document.createRange();
        range.selectNode(document.getElementById("selection-start"));
        selection.addRange(range);

        range = document.createRange();
        range.selectNode(document.getElementById("selection-end"));
        selection.addRange(range);

        var result = await testScrollRepaint("Overflow scroll with selection", false, container);

        getSelection().removeAllRanges();
        return result;
    }

    async function testMarquee() {
        container.style.display = "none";
        marquee.style.display = "block";
        var result = await testScrollRepaint("Marquee", false, marquee);
        marquee.style.display = "none";
        container.style.display = "block";
        return result;
    }

    async function testWithInlineChild() {
        span.style.display = "inline";
        var result = await testScrollRepaint("Overflow scroll with inline child", false, container);
        span.style.display = "none";
        return result;
    }

    async function testOverflowHidden() {
        container.style.overflow = "hidden";
        var result = testScrollRepaint("Overflow hidden", false, container);
        container.style.overflow = "scroll";
        return result;
    }

    async function doTests() {
        marquee.stop();
        var result = await testNoRepaint();
        result += await testWithSelection();
        result += await testMarquee();
        result += await testWithInlineChild();
        result += await testOverflowHidden();
        if (window.testRunner) {
            testRunner.setCustomTextOutput(result);
            testRunner.notifyDone();
        }
    }

    window.onload = doTests;
</script>
<style>
    #target::selection { background-color: green; }

    #container {
        width:100px;
        height:300px;
        border: 1px black solid;
        overflow: scroll;
        backface-visibility: hidden;
        position: relative;
    }

    #fixed {
        width: 50px;
        height: 50px;
        position: fixed;
        top: 200px;
        left: 200px;
        background-color: blue;
        backface-visibility: hidden;
    }

    .scrolled {
        width: 50px;
        height: 50px;
        margin: 10px;
        position: relative;
        background-color: green;
        backface-visibility: hidden;
    }

    #span {
        display: none;
    }

    #description {
        display: none;
    }

    marquee {
        width: 60px;
        height: 60px;
        backface-visibility: hidden;
        position: relative;
        display: none;
    }
</style>
</head>
<body>
    <pre id="description">
      This test ensures that if the only thing that scrolls is a composited layer,
      we do not repaint. It passes if we repaint when we have to draw the selection
      block gaps or if we have content that is not in a composited layer. It also
      checks that we do not repaint when all the content is in a composited layer.
    </pre>
    <div id="container">
        <span id="span">Hello!</span>
        <div id="fixed"></div>
        <div class="scrolled" id="selection-start"></div>
        <div class="scrolled" id="selection-end"></div>
        <div class="scrolled"></div>
        <div class="scrolled"></div>
        <div class="scrolled"></div>
        <div class="scrolled"></div>
        <div class="scrolled"></div>
        <div class="scrolled"></div>
        <div class="scrolled"></div>
    </div>
    <marquee id="marquee" direction="up" scrollamount="1">
        <p>Lorem ipsum dolor amet</p>
        <p>Lorem ipsum dolor amet</p>
        <p>Lorem ipsum dolor amet</p>
        <p>Lorem ipsum dolor amet</p>
        <p>Lorem ipsum dolor amet</p>
        <p>Lorem ipsum dolor amet</p>
        <p>Lorem ipsum dolor amet</p>
    </marquee>
</body>
</html>
